Sync DOORS tables

Hi,

I'm trying to create a script that would allow me to revert back to a previous baseline, I was wondering what would the best approach be; I would like to re-sync the DOORS Tables by recreating the missing parts if they were purge. I already have the logic to recreate the entire table if completely missing. I would like to re-sync the tables if they still exist, this would reduce the number of object being created. Any help would be appreciated. Thank you.

pragma encoding, "UTF-8"
pragma runLim, 0

Module src = edit("/baselineSet/Sync", true, true, true)

if (!exists attribute "Sync Absolute Number")
        create object type "Integer" attribute "Sync Absolute Number"

if (!exists attribute "Sync Absolute Number")
        halt

Baseline b = getMostRecentBaseline(src) // get last Baseline.
Module trg = load(src, b, false) // open baseline module.

Skip skTargets = create ()

Object byAbsNo(int iAbsNo, Module m) {
        Object o = object (iAbsNo, m)
        if (null(o)) {
                Object oSearch
                for oSearch in entire m do {
                        int nr = oSearch."Sync Absolute Number"
                        if (nr == 0) nr = oSearch."Absolute Number"
                        if (nr == iAbsNo) { o = oSearch; break }
                }
        }
        return(o)
} // byAbsNo

void setObj(Object trg, Object src) {
        AttrDef ad = null
        Module m = module(trg)
        string ATTRNAME = null

        for ad in m do {
                if (!ad.object) continue
                if (!ad.useraccess) continue
                
                ATTRNAME = ad.name
                set(trg.ATTRNAME, src.ATTRNAME)
        }

        int nr = src."Absolute Number"
        trg."Sync Absolute Number" = nr

        if (cell(trg)) {
                setCellWidth(trg, getCellWidth(src))
                setCellBorder(trg, left, getCellBorder(src, left))
                setCellBorder(trg, right, getCellBorder(src, right))
                setCellBorder(trg, top, getCellBorder(src, top))
                setCellBorder(trg, bottom, getCellBorder(src, bottom))
                setCellShowLinkArrows(trg, getCellShowLinkArrows(src))
                setCellShowChangeBars(trg, getCellShowChangeBars(src))
                setCellAlignment(trg, getCellAlignment(src))
        }
}

void initialSync(Module src, Module trg) {
        Object o = null
        for o in entire trg do {
                if (null(o)) continue
                if (isDeleted(o)) continue
                if (table(o) || row(o) || cell(o)) continue
                int nr = o."Absolute Number"
                put(skTargets, nr, o)           
        }

        // retore existing objects...
        for o in entire src do {
                if (null(o)) continue
                if (row(o) || cell(o)) continue
                if (table(o)) {deleteTable(o); continue}
                int nr = o."Sync Absolute Number"
                if (nr == 0) nr = o."Absolute Number"

                Object oTarget = null
                // match the objects in the skip lists. Key: Absolute Number Value: Object in Target Module
                if (find(skTargets, nr, oTarget)) {
                        undelete(o)
                        setObj(o, oTarget)
                } else if (!isDeleted(o)) {
                        softDelete(o, false)
                }
        }
}

//prevent unneeded creation of new objects...
Object getNextObject(Module m, Object oSrc) {
        Object o = null
        Object oSearch
        for oSearch in entire m do {
                if (null(oSearch)) continue
                if (!isDeleted(oSearch)) continue
                if (table(oSearch) || row(oSearch) || cell(oSearch)) continue

                o = oSearch
                undelete(o)
                break
        }

        if (null(o)) o = create m

        return(o)
}

Object createTable(Object tbl, int c, int r) {
        current = module(tbl)
        return table(tbl, c, r)
}

Object syncTable(Module src, Object oldTable) {

        Object oldRow = null, oldCell = null
        Object newTable = null, newRow = null, newCell = null

        int c = 0, r = 0
        Array a = create(1,1)

        for oldRow in table(oldTable) do {
                c = 0
                for oldCell in row(oldRow) do {
                        put(a, (Object oldCell), c++, r)
                }
                r++
        }

        Object o1 = first src
        int nr = oldTable."Absolute Number"
        newTable = byAbsNo(nr, src)

        if (!null(newTable)) {
                deleteTable(newTable)
        }

        newTable = createTable(o1, c, r)
         setObj(newTable, oldTable)
        // Display table in Main Column only
        setShowTableAcrossModule(newTable, getShowTableAcrossModule(oldTable))
        // Get the name of the currently displayed attr in this table
        string NameAttr = oldTable.(reserved "Main Column Attribute")
         newTable.(reserved "Main Column Attribute") = NameAttr
        c = 0
        r = 0

        for newRow in table(newTable) do {
                c = 0
                for newCell in row(newRow) do {
                        Object oldCell = (Object get(a, c++, r))
                         setObj(newCell, oldCell)
                }
                r++
        }

        delete a
        return newTable
}

void cleanSync(Module src) {
        Object o = null

        for o in entire src do {
                if (null(o)) continue
                if (!isDeleted(o)) continue
                if (row(o) || cell(o)) continue

                if (table(o)) {
                        undeleteTable(o)
                        Object tblRow = null, tblCell = null
                        
                        for tblRow in table(o) do {
                                for tblCell in row(tblRow) do {
                                        tblCell."Sync Absolute Number" = 0
                                }
                                tblRow."Sync Absolute Number" = 0
                        }
                        o."Sync Absolute Number" = 0
                        deleteTable(o)
                } else {
                        undelete(o)
                        o."Sync Absolute Number" = 0
                        softDelete(o, false)
                }
        }
}

void syncModule(Module src, Module trg) {
        Object oLastTarget = null, o = null
        int iOldLevel = 0, cnt = 0

        setempty(skTargets)
        for o in entire src do {
                if (null(o)) continue
                if (row(o) || cell(o)) continue // can't move row/cells
                int nr = o."Sync Absolute Number"
                put(skTargets, nr, o)           
        }
        //https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014873078&ps=25
        //https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014780591
        //https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000014167939&ps=25
        // iterate through the baseline
        for o in entire trg do {
                if (null(o)) continue
                if (isDeleted(o)) continue
                if (row(o) || cell(o)) continue // can't move row/cells

                int nr = o."Absolute Number"
                Object oTarget = null
                // match the objects in the skip lists. Key: Absolute Number Value: Object in Target Module
                if (table(o)) {
                        oTarget = syncTable(src, o)
                } else if (!find(skTargets, nr, oTarget)) {
                        oTarget = getNextObject(src, o)
                        setObj(oTarget, o)
                }

                if (null(oTarget)) {
                        // TBD, XXX: How to handle error?
                        //print "Not found: " nr "\n"
                        continue
                }
                 

                if (cnt ++ == 0) {
                        // move the first object to the start of the module, it only needs to be on top level. 
                        if (oTarget != first src) move (oTarget, after first src)
                        iOldLevel = 1
                        oLastTarget = oTarget
                } else {
                        // move all other objects relative ...            
                        int iSrcLevel = level o

                        if (iSrcLevel == iOldLevel + 1) {
                                // The next object has a higher level, so move the next object below the last one
                                move (oTarget, below oLastTarget)
                        } else if (iSrcLevel == iOldLevel) {
                                // The next object has the same level, so move the next object after the last one
                                move (oTarget, after oLastTarget)
                        } else if (iSrcLevel < iOldLevel) {
                                // The next object has smaller level, so we need to move after one of the last parents
                                // iSrcLevel < iOldLevel -> e.g. Move back from Level 3 to level 1
                                // Therefore we will move up parents by the difference 
                                Object oTgtParent = oLastTarget
                                int i; for (i = 0; i < iOldLevel - iSrcLevel; i++) oTgtParent = parent oTgtParent
                                move (oTarget, after oTgtParent)
                        } else {
                                error "WTH: " (identifier oTarget) " - " (iSrcLevel) " - " iOldLevel "\n"
                        }
                        iOldLevel   = iSrcLevel
                        oLastTarget = oTarget
                }    
        }

        refresh(src)
        refreshExplorer(src)
}

initialSync(src, trg)
syncModule(src, trg)
cleanSync(src)

delete skTargets

ack "Done!"

 


EHcnck - Sun Feb 12 12:28:30 EST 2017

Re: Sync DOORS tables
Mathias Mamsch - Tue Feb 14 04:19:45 EST 2017

Resyncing tables is the same as resyncing objects, with the difference of different perms that you need for creating the objects. You would first create all missing objects, delete all obsolete objects, reorder the objects and then sync the attribute values. The code should be pretty straight forward. 

What problem are you exactly facing? Regards, Mathias

Re: Sync DOORS tables
EHcnck - Tue Feb 14 07:44:35 EST 2017

Mathias Mamsch - Tue Feb 14 04:19:45 EST 2017

Resyncing tables is the same as resyncing objects, with the difference of different perms that you need for creating the objects. You would first create all missing objects, delete all obsolete objects, reorder the objects and then sync the attribute values. The code should be pretty straight forward. 

What problem are you exactly facing? Regards, Mathias

I have a function to copy a missing DOORS table, just wondering what is the best approach to sync the table if it does exist; meaning if we need to insert vs append (rows vs cells) etc.

Object copyTable(Module src, Object oldTable) {
        Object oldRow = null, oldCell = null
        Object newRow = null, newCell = null
        Object startTable = create src

        for oldRow in table(oldTable) do {
                if(null(oldRow) || isDeleted(oldRow)) continue
                 newRow = create(last(below(startTable))); setObj(newRow, oldRow)
                for oldCell in row(oldRow) do {
                        if(null(oldCell) || isDeleted(oldCell)) continue
                         newCell = create(last(below(newRow))); setObj(newCell, oldCell)
                }
        }
        toTable startTable
        return startTable
}

 

Re: Sync DOORS tables
Mathias Mamsch - Wed Feb 15 03:11:12 EST 2017

EHcnck - Tue Feb 14 07:44:35 EST 2017

I have a function to copy a missing DOORS table, just wondering what is the best approach to sync the table if it does exist; meaning if we need to insert vs append (rows vs cells) etc.

Object copyTable(Module src, Object oldTable) {
        Object oldRow = null, oldCell = null
        Object newRow = null, newCell = null
        Object startTable = create src

        for oldRow in table(oldTable) do {
                if(null(oldRow) || isDeleted(oldRow)) continue
                 newRow = create(last(below(startTable))); setObj(newRow, oldRow)
                for oldCell in row(oldRow) do {
                        if(null(oldCell) || isDeleted(oldCell)) continue
                         newCell = create(last(below(newRow))); setObj(newCell, oldCell)
                }
        }
        toTable startTable
        return startTable
}

 

I do not think, that you need to separate between updating and creating a table. It should be all the same. Your code could be structured like this: 

Skip skBaseline = getObjectsInModule(modBase); 

Skip skCurrent = getObjectsInCurrent(modCurrent); 

Object findCurrentObjectForBaseline (Skip skBase, Object o) { /* ... */ }

void getObjectLocation(Object o, Object &oPrevious, Object &oParent) {
  // find the location of the object in the current module, return 
  // the previous and parent object 
  // ...
}

void createMissingObjects(Skip skCurrent, Skip skBaseline) {
    Object oBase, oCurrent; 
    
    for oBase in skBaseline do {
        oCurrent = findCurrentObjectForBaseline (skCurrent, oBase)
        
        Object oPrevious, oParent; 
        getObjectLocation(oBase, oPrevious, oParent); 
        
        if (null oCurrent) {
            if (table oBase) {
                // create a table
                if (oPrevious) ...
                else if (oParent) ...
                else ...                
            } else if (row oBase) {
                // create table
                if (oPrevious) ...
                else if (oParent) ...
                else ...
            } else if (cell oBase) {
                // create cell
                if (oPrevious) ...
                else if (oParent) ...
                else error "WTF?"
            } else {
                // regular object
                if (oPrevious) oCurrent = create after oPrevious; 
                else if (oParent) oCurrent = create below oParent; 
                else oCurrent = create modCurrent // create first in module
            }
            
            put(skCurrent, ..., oCurrent); 
        }
    }
}

createMissingObjects(skCurrent, skBaseline); 
deleteObsoleteObjects(skCurrent, skBaseline);
reorderObjects(skCurrent, skBaseline);

Of course you need to take care about things like "createRow" also creating the first cell (purge it?) and you need to handle the problem of "reusing" existing objects that are not in the baseline and have been created in a previous run of the script. If you dont care simply purge everything, that has not the same absolute number as in the baseline.

But I hope that makes clear, why I see no difference between update and create - this code will create the baseline also from scratch if no objects exist in the current module. 

Regards, Mathias

Re: Sync DOORS tables
EHcnck - Wed Feb 15 07:30:28 EST 2017

Mathias Mamsch - Wed Feb 15 03:11:12 EST 2017

I do not think, that you need to separate between updating and creating a table. It should be all the same. Your code could be structured like this: 

Skip skBaseline = getObjectsInModule(modBase); 

Skip skCurrent = getObjectsInCurrent(modCurrent); 

Object findCurrentObjectForBaseline (Skip skBase, Object o) { /* ... */ }

void getObjectLocation(Object o, Object &oPrevious, Object &oParent) {
  // find the location of the object in the current module, return 
  // the previous and parent object 
  // ...
}

void createMissingObjects(Skip skCurrent, Skip skBaseline) {
    Object oBase, oCurrent; 
    
    for oBase in skBaseline do {
        oCurrent = findCurrentObjectForBaseline (skCurrent, oBase)
        
        Object oPrevious, oParent; 
        getObjectLocation(oBase, oPrevious, oParent); 
        
        if (null oCurrent) {
            if (table oBase) {
                // create a table
                if (oPrevious) ...
                else if (oParent) ...
                else ...                
            } else if (row oBase) {
                // create table
                if (oPrevious) ...
                else if (oParent) ...
                else ...
            } else if (cell oBase) {
                // create cell
                if (oPrevious) ...
                else if (oParent) ...
                else error "WTF?"
            } else {
                // regular object
                if (oPrevious) oCurrent = create after oPrevious; 
                else if (oParent) oCurrent = create below oParent; 
                else oCurrent = create modCurrent // create first in module
            }
            
            put(skCurrent, ..., oCurrent); 
        }
    }
}

createMissingObjects(skCurrent, skBaseline); 
deleteObsoleteObjects(skCurrent, skBaseline);
reorderObjects(skCurrent, skBaseline);

Of course you need to take care about things like "createRow" also creating the first cell (purge it?) and you need to handle the problem of "reusing" existing objects that are not in the baseline and have been created in a previous run of the script. If you dont care simply purge everything, that has not the same absolute number as in the baseline.

But I hope that makes clear, why I see no difference between update and create - this code will create the baseline also from scratch if no objects exist in the current module. 

Regards, Mathias

I thought you couldn't reorder table with the move() cmd.

Re: Sync DOORS tables
Mathias Mamsch - Wed Feb 15 07:48:47 EST 2017

EHcnck - Wed Feb 15 07:30:28 EST 2017

I thought you couldn't reorder table with the move() cmd.

True for table cells inside a table - you can move a whole table though. In case of a moved table cell, you would need to resort  to  a "purge/create" operation to implement the move. But in the end, since you cannot move a table cell from the DOORS GUI anyway, you will probably not run into a move condition for your table cells so often. 

It all boils down to the following question(s):

- Do you consider it bad, that all objects that were created after the baseline will be purged and objects that were purged after the baseline will be recreated?

- Do you want to reuse the objects that were created after the baseline during the restore to replace the objects that were created after the baseline? 

Regards, Mathias

 

Re: Sync DOORS tables
EHcnck - Wed Feb 15 08:38:44 EST 2017

Mathias Mamsch - Wed Feb 15 07:48:47 EST 2017

True for table cells inside a table - you can move a whole table though. In case of a moved table cell, you would need to resort  to  a "purge/create" operation to implement the move. But in the end, since you cannot move a table cell from the DOORS GUI anyway, you will probably not run into a move condition for your table cells so often. 

It all boils down to the following question(s):

- Do you consider it bad, that all objects that were created after the baseline will be purged and objects that were purged after the baseline will be recreated?

- Do you want to reuse the objects that were created after the baseline during the restore to replace the objects that were created after the baseline? 

Regards, Mathias

 

for recreating a row that is irregular I would first use insert/appendRow then use insert/appendCell is there a way of creating the new row with using insert/appendCell and (first, after...) it would limit the unneeded creation of additional cells in some cases? Thank you.

Re: Sync DOORS tables
Mathias Mamsch - Wed Feb 15 09:06:06 EST 2017

EHcnck - Wed Feb 15 08:38:44 EST 2017

for recreating a row that is irregular I would first use insert/appendRow then use insert/appendCell is there a way of creating the new row with using insert/appendCell and (first, after...) it would limit the unneeded creation of additional cells in some cases? Thank you.

Can you specify "some cases" ?

I would think, there is only one valid reason to create a row in your script: The row has existed in the baseline but the user has purged it since then.

If this is the case, obviously all the cells inside the row have been purged too! Therefore you need to insert a new row (watch out, it will automatically create one cell), so after inserting the row you need to associate the created cell with the first cell in the baseline row). 

In this case you will not create a single unneeded object. 

I think you are making this in your mind to complicated, like "in the baseline the table had 3 cells, in the current it has 5 cells, but two cells already existed in the baseline ... So which one is what, and what do I need to create/delete" ... It is really easy: 

  • if the absolute number of the cell in the current module already exists in the baseline, then you leave it.
  • If not you delete it. 
  • if in the baseline there is an absno, which is not in the current module anymore (purged), then you create it. 

Now since you seem to care about the existing objects lets take a worst case example: 

- A user purged a table from the module, and recreated it manually.

So now the table is there and it matches the baseline, but it will be purged and recreated. If you consider this unbearable (oh, no, this will make the absolute numbers grow to fast!) then you would need to do one additional step before you run the exact algorithm I described above: 

After matching all objects with the same absolute number, you will need to match the remaining objects of current and baseline. When you matched the objects, then you proceed as discussed. 

Regards, Mathias

Re: Sync DOORS tables
EHcnck - Wed Feb 15 09:19:40 EST 2017

Mathias Mamsch - Wed Feb 15 09:06:06 EST 2017

Can you specify "some cases" ?

I would think, there is only one valid reason to create a row in your script: The row has existed in the baseline but the user has purged it since then.

If this is the case, obviously all the cells inside the row have been purged too! Therefore you need to insert a new row (watch out, it will automatically create one cell), so after inserting the row you need to associate the created cell with the first cell in the baseline row). 

In this case you will not create a single unneeded object. 

I think you are making this in your mind to complicated, like "in the baseline the table had 3 cells, in the current it has 5 cells, but two cells already existed in the baseline ... So which one is what, and what do I need to create/delete" ... It is really easy: 

  • if the absolute number of the cell in the current module already exists in the baseline, then you leave it.
  • If not you delete it. 
  • if in the baseline there is an absno, which is not in the current module anymore (purged), then you create it. 

Now since you seem to care about the existing objects lets take a worst case example: 

- A user purged a table from the module, and recreated it manually.

So now the table is there and it matches the baseline, but it will be purged and recreated. If you consider this unbearable (oh, no, this will make the absolute numbers grow to fast!) then you would need to do one additional step before you run the exact algorithm I described above: 

After matching all objects with the same absolute number, you will need to match the remaining objects of current and baseline. When you matched the objects, then you proceed as discussed. 

Regards, Mathias

"Therefore you need to insert a new row (watch out, it will automatically create one cell), so after inserting the row you need to associate the created cell with the first cell in the baseline row). " All the versions of DOORS I've used will create the same number of cells that your based/starting object(row) has when using append/insertRow. 

Re: Sync DOORS tables
Mathias Mamsch - Wed Feb 15 10:09:56 EST 2017

EHcnck - Wed Feb 15 09:19:40 EST 2017

"Therefore you need to insert a new row (watch out, it will automatically create one cell), so after inserting the row you need to associate the created cell with the first cell in the baseline row). " All the versions of DOORS I've used will create the same number of cells that your based/starting object(row) has when using append/insertRow. 

Possible. I cannot really tell if your question is answered and you seem not to be satisfied with the answers I can give you. I understood your original question as " how can I sync tables" and my answer was "the same way you sync objects". If you need something (code example, etc.) you need to state that clearly. I am not sure If I am helping you at all. Regards, Mathias

Re: Sync DOORS tables
EHcnck - Wed Feb 15 17:36:26 EST 2017

Mathias Mamsch - Wed Feb 15 10:09:56 EST 2017

Possible. I cannot really tell if your question is answered and you seem not to be satisfied with the answers I can give you. I understood your original question as " how can I sync tables" and my answer was "the same way you sync objects". If you need something (code example, etc.) you need to state that clearly. I am not sure If I am helping you at all. Regards, Mathias

 Trying to determine the best approach/most efficient way of recreating the data for missing cells('X'); where cell('X') was purged from the Current Module?

 

X  X   X
X   Text  X
 Text  Text  Text
Text X X